﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media.Animation;

namespace LogoFX.Controls
{
    public class NotificationContainer : ContentItemsControl
    {
        #region Fields

        private static readonly Type s_thisType = typeof(NotificationContainer);

        private readonly Dictionary<NotificationItem, Storyboard> _hidingItems =
            new Dictionary<NotificationItem, Storyboard>();

        #endregion

        #region Constructors

        public NotificationContainer()
        {
            DefaultStyleKey = typeof(NotificationContainer);

            Loaded += NotificationContainerLoaded;
            Unloaded += NotificationContainerUnloaded;

        }

        void NotificationContainerUnloaded(object sender, RoutedEventArgs e)
        {
            NotificationsService notificationsService = NotificationsService.Instance;
            
            notificationsService.NotificationShowing -= NotificationsServiceNotificationShowing;
            notificationsService.NotificationClosing -= NotificationsServiceNotificationClosing;
        }

        void NotificationContainerLoaded(object sender, RoutedEventArgs e)
        {
            NotificationsService notificationsService = NotificationsService.Instance;
            SetBinding(ItemsSourceProperty, CreateBinding("Notifications", notificationsService));
            notificationsService.NotificationShowing += NotificationsServiceNotificationShowing;
            notificationsService.NotificationClosing += NotificationsServiceNotificationClosing;
        }

        #endregion

        #region Dependency Properties

        public static readonly DependencyProperty ItemHeaderTemplateProperty =
            DependencyProperty.Register(
                "ItemHeaderTemplate",
                typeof(DataTemplate),
                s_thisType,
                new PropertyMetadata(null));

        public static readonly DependencyProperty FadeDurationProperty =
            DependencyProperty.Register(
                "FadeDuration",
                typeof(TimeSpan),
                s_thisType,
                new PropertyMetadata(
                    TimeSpan.FromMilliseconds(1000)));

        public static readonly DependencyProperty AttachedNotificationProperty =
            DependencyProperty.RegisterAttached(
                "AttachedNotification",
                typeof(object),
                s_thisType,
                new PropertyMetadata(null));

        public static object GetAttachedNotification(DependencyObject obj)
        {
            return obj.GetValue(AttachedNotificationProperty);
        }

        public static void SetAttachedNotification(DependencyObject obj, object value)
        {
            obj.SetValue(AttachedNotificationProperty, value);
        }



        #endregion

        #region Public Properties

        public DataTemplate ItemHeaderTemplate
        {
            get { return (DataTemplate)GetValue(ItemHeaderTemplateProperty); }
            set { SetValue(ItemHeaderTemplateProperty, value); }
        }

        public TimeSpan FadeDuration
        {
            get { return (TimeSpan)GetValue(FadeDurationProperty); }
            set { SetValue(FadeDurationProperty, value); }
        }

        #endregion

        #region Overrides

        protected override DependencyObject GetContainerForItemOverride()
        {
            NotificationItem item = new NotificationItem();

            return item;
        }

        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);

            BindingOperations.SetBinding(element, ContentControl.ContentTemplateProperty,
                                         CreateBinding("ItemTemplate", this));
            BindingOperations.SetBinding(element, HeaderedContentControl.HeaderTemplateProperty,
                                         CreateBinding("ItemHeaderTemplate", this));
            BindingOperations.SetBinding(element, HeaderedContentControl.HeaderProperty,
                                         CreateBinding("DataObject", item));
            BindingOperations.SetBinding(element, ContentControl.ContentProperty,
                                         CreateBinding("DataObject", item));
            BindingOperations.SetBinding(element, NotificationItem.CookieProperty,
                                         CreateBinding("Cookie", item));
            BindingOperations.SetBinding(element, NotificationItem.PinedProperty,
                                        CreateBinding("Pined", item, BindingMode.TwoWay));

            ((NotificationItem)element).MouseEnter += NotificationContainer_MouseEnter;
            ((NotificationItem)element).MouseLeave +=NotificationContainer_MouseLeave;
        }

        #endregion

        #region Private Members

        private static Binding CreateBinding(string sourcePropertyPath, object source = null, BindingMode mode = BindingMode.OneWay)
        {
            Binding binding = new Binding(sourcePropertyPath)
                                  {
                                      Source = source,
                                      Mode = mode
                                  };

            return binding;
        }

        private void NotificationsServiceNotificationClosing(object sender, NotificationServiceItemEventArgs e)
        {
            NotificationItem container = (NotificationItem)ItemContainerGenerator.ContainerFromItem(e.Notification);

            if (container == null || _hidingItems.ContainsKey(container))
            {
                return;
            }

            DoubleAnimation fadeOutAnimation = new DoubleAnimation();
            fadeOutAnimation.Duration = FadeDuration;
            fadeOutAnimation.From = 1d;
            fadeOutAnimation.To = 0d;
            //fadeOutAnimation.EasingFunction = new CubicEase();
            Storyboard.SetTarget(fadeOutAnimation, container);
            Storyboard.SetTargetProperty(fadeOutAnimation, new PropertyPath(NotificationItem.OpacityProperty));

            Storyboard storyboard = new Storyboard();
            storyboard.SetValue(AttachedNotificationProperty, e.Notification);
            storyboard.Completed += FadeOutCompleted;
            storyboard.Children.Add(fadeOutAnimation);            

            _hidingItems.Add(container, storyboard);

            storyboard.Begin();
        }

        private void FadeOutCompleted(object sender, EventArgs e)
        {
            Notification notification = (Notification)((DependencyObject)sender).GetValue(AttachedNotificationProperty);
            NotificationItem container = (NotificationItem)ItemContainerGenerator.ContainerFromItem(notification);

            if (container == null)
            {
                DownCompleted(sender, e);
                return;
            }

            if (!_hidingItems.ContainsKey(container))
            {
                return;
            }

            _hidingItems.Remove(container);

            DoubleAnimation downAnimation = new DoubleAnimation();
            downAnimation.Duration = FadeDuration;
            downAnimation.From = container.ActualHeight;
            downAnimation.To = 1d;
           // downAnimation.EasingFunction = new ElasticEase();
            Storyboard.SetTarget(downAnimation, container);
            Storyboard.SetTargetProperty(downAnimation, new PropertyPath(NotificationContainerPanel.DisappearHeightProperty));

            Storyboard storyboard = new Storyboard();
            storyboard.SetValue(AttachedNotificationProperty, notification);
            storyboard.Completed += DownCompleted;
            storyboard.Children.Add(downAnimation);

            NotificationsService.Instance.StartClose(notification);

            storyboard.Begin();
        }

        private void DownCompleted(object sender, EventArgs e)
        {
            Notification notification = (Notification)((DependencyObject)sender).GetValue(AttachedNotificationProperty);
            NotificationsService.Instance.Hide(notification.Cookie);
        }

        private void NotificationsServiceNotificationShowing(object sender, NotificationServiceItemEventArgs e)
        {
            NotificationItem container = (NotificationItem)ItemContainerGenerator.ContainerFromItem(e.Notification);

            if (_hidingItems.ContainsKey(container))
            {
                StopClosing(container);
                return;
            }

            DoubleAnimation fadeInAnimation = new DoubleAnimation();
            fadeInAnimation.Duration = FadeDuration;
            fadeInAnimation.From = 0d;
            fadeInAnimation.To = 1d;
            Storyboard.SetTarget(fadeInAnimation, container);
            Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath(OpacityProperty));

            Storyboard storyboard = new Storyboard();
            storyboard.Children.Add(fadeInAnimation);
            storyboard.Begin();
        }

        private void StopClosing(NotificationItem notificationItem)
        {
            Storyboard storyboard;

            if (!_hidingItems.TryGetValue(notificationItem, out storyboard))
            {
                return;
            }

            _hidingItems.Remove(notificationItem);
            storyboard.Stop();
            notificationItem.Opacity = 1.0;
        }

        private void NotificationContainer_MouseEnter(object sender, MouseEventArgs e)
        {
            NotificationItem notificationItem = (NotificationItem)sender;
            Notification notification = ItemContainerGenerator.ItemFromContainer(notificationItem) as Notification;

            if (notification == null)
            {
                return;
            }

            if (_hidingItems.ContainsKey(notificationItem))
            {
                StopClosing(notificationItem);
            }
            {
                notification.StopTimer();
            }
        }

        private void NotificationContainer_MouseLeave(object sender, MouseEventArgs e)
        {
            NotificationItem notificationItem = (NotificationItem)sender;
            Notification notification = (Notification)ItemContainerGenerator.ItemFromContainer(notificationItem);

            if (notification == null)
            {
                return;
            }

            notification.StartTimer();
        }

        #endregion
    }
}